import { clamp } from './number';
import { escapeRegExp } from './regexp';
/**
 * 判断是否为闰年。
 */
export function isLeapYear(year) {
    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
const monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
/**
 * 获取某月的天数，月份从0开始。
 */
export function getDaysInMonth(year, month) {
    if (month === 1) {
        return isLeapYear(year) ? 29 : 28;
    }
    else {
        return monthDays[month];
    }
}
/**
 * 获取当前日期是一年中的第几天。
 */
export function getDayOfYear(date) {
    const year = date.getFullYear();
    const month = date.getMonth();
    const d = date.getDate();
    let days = 0;
    for (let m = 0; m < month; m++) {
        days += getDaysInMonth(year, m);
    }
    return days + d;
}
/**
 * 获取指定月份第一天是星期几，月份从0开始。
 */
export function getFirstDayWeekday(year, month) {
    return new Date(year, month, 1).getDay();
}
/**
 * 获取从基准日期（如1970-01-01）到指定日期的总天数。
 */
export function getDaysSinceUnixEpoch(date) {
    return Math.floor(date.getTime() / 1000 / 60 / 60 / 24);
}
/**
 * 把日期转换为年月日数值，例如：2025年7月1号 -> 20250601。
 */
export function toDateNumber(date) {
    return date.getFullYear() * 10000 + date.getMonth() * 100 + date.getDate();
}
/**
 * 把日期转换为年月日字符串，，例如：2025年7月1号 -> 2025-7-1。
 */
export function toDateString(date) {
    return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
}
/**
 * 把日期转换为年月数值，例如：2025年7月1号 -> 20250600。
 */
export function toMonthNumber(date) {
    return date.getFullYear() * 10000 + date.getMonth() * 100;
}
/**
 * 计算当前月份1号前面需要显示的上个月末尾的天数。
 */
export function getDaysBeforeFirstDay(year, month, weekStartsOn = 0) {
    const week = getFirstDayWeekday(year, month);
    return (week - weekStartsOn + 7) % 7;
}
/**
 * 获取当前月份最后一天之后需要显示的下个月开始的天数。
 */
export function getDaysAfterLastDay(year, month, weekStartsOn = 0) {
    const daysBefore = getDaysBeforeFirstDay(year, month, weekStartsOn);
    return 42 - daysBefore - getDaysInMonth(year, month);
}
/**
 * 获取当前月份1号之前需要显示的上个月末尾的日期。
 */
export function getPrevMonthTailDays(year, month, weekStartsOn = 0) {
    const daysBefore = getDaysBeforeFirstDay(year, month, weekStartsOn);
    const dates = [];
    for (let i = daysBefore - 1; i >= 0; i--) {
        dates.push(new Date(year, month, -i));
    }
    return dates;
}
/**
 * 获取当前月份最后一天之后需要显示的下个月开始的日期。
 */
export function getNextMonthHeadDays(year, month, weekStartsOn = 0) {
    const daysAfter = getDaysAfterLastDay(year, month, weekStartsOn);
    const dates = [];
    for (let i = 1; i <= daysAfter; i++) {
        dates.push(new Date(year, month + 1, i));
    }
    return dates;
}
const dateTokensReg = /(YYYY|YY|MM|M|DD|D|HH|H|hh|h|mm|m|ss|s|SSS)/g;
const dateGetters = {
    YYYY: (date) => String(date.getFullYear()).padStart(4, '0'),
    YY: (date) => String(date.getFullYear()).slice(-2),
    MM: (date) => String(date.getMonth() + 1).padStart(2, '0'),
    M: (date) => String(date.getMonth() + 1),
    DD: (date) => String(date.getDate()).padStart(2, '0'),
    D: (date) => String(date.getDate()),
    HH: (date) => String(date.getHours()).padStart(2, '0'),
    H: (date) => String(date.getHours()),
    hh: (date) => String(date.getHours() % 24).padStart(2, '0'),
    h: (date) => String(date.getHours() % 24),
    mm: (date) => String(date.getMinutes()).padStart(2, '0'),
    m: (date) => String(date.getMinutes()),
    ss: (date) => String(date.getSeconds()).padStart(2, '0'),
    s: (date) => String(date.getSeconds()),
    SSS: (date) => String(date.getMilliseconds()).padStart(3, '0'),
};
/**
 * 根据传入的占位符返回格式化后的日期。
 */
export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
    return format.replace(dateTokensReg, (match) => {
        return dateGetters[match](date);
    });
}
const defaultTokenFrags = ['YYYY', 'MM', 'DD', 'HH', 'mm', 'ss', 'SSS'];
const dateSetters = {
    YYYY: (date, year) => date.setFullYear(year),
    YY: (date, year) => date.setFullYear(year),
    MM: (date, month) => date.setMonth(month - 1),
    M: (date, month) => date.setMonth(month - 1),
    DD: (date, d) => date.setDate(d),
    D: (date, d) => date.setDate(d),
    HH: (date, hours) => date.setHours(hours),
    H: (date, hours) => date.setHours(hours),
    hh: (date, hours) => date.setHours(hours),
    h: (date, hours) => date.setHours(hours),
    mm: (date, minutes) => date.setMinutes(minutes),
    m: (date, minutes) => date.setMinutes(minutes),
    ss: (date, seconds) => date.setSeconds(seconds),
    s: (date, seconds) => date.setSeconds(seconds),
    SSS: (date, milliseconds) => date.setMilliseconds(milliseconds),
};
function parseDateWithoutFormat(value) {
    const numbers = value.split(/\D+/).filter(Boolean);
    const tokens = defaultTokenFrags.slice(0, numbers.length);
    const date = new Date(0, 0);
    tokens.forEach((token, i) => {
        dateSetters[token](date, Number(numbers[i]));
    });
    return date;
}
/**
 * 解析日期的字符串表示形式，并返回 Date 实例对象。
 */
export function parseDate(value, format) {
    if (!format) {
        return parseDateWithoutFormat(value);
    }
    const frags = format.split(dateTokensReg);
    const reg = new RegExp('^' +
        frags
            .filter((_, i) => i % 2 === 0)
            .map((item) => escapeRegExp(item))
            .join('(.*)') +
        '$');
    const dateFrags = reg.exec(value)?.slice(1);
    if (!dateFrags) {
        return parseDateWithoutFormat(value);
    }
    const tokenFrags = frags.filter((_, i) => i % 2 !== 0);
    const date = new Date(0);
    tokenFrags.forEach((token, i) => {
        dateSetters[token](date, Number(dateFrags[i]));
    });
    return date;
}
// 确保返回一个Date对象，如果传递字符串，则使用 parseDate 解析。
export function toDate(date, valueFormat) {
    if (date instanceof Date) {
        return date;
    }
    return parseDate(date, valueFormat);
}
// 限定日期范围，会返回一个新的日期。
export function minmaxDate(date, minDate, maxDate) {
    return new Date(clamp(date.getTime(), minDate.getTime(), maxDate.getTime()));
}
// 获取上一个月的日期对象。
export function getPrevMonthDate(date) {
    return new Date(date.getFullYear(), date.getMonth() - 1, 1);
}
// 获取下一个月的日期对象。
export function getNextMonthDate(date) {
    return new Date(date.getFullYear(), date.getMonth() + 1, 1);
}
/****************************************************************
 * 农历
 ****************************************************************/
// prettier-ignore
export const lunarInfo = [
    0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
    0x06566, 0x0d4a0, 0x0ea50, 0x16a95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
    0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
    0x0d520, // 2100
];
// 1900-2100春节对应的公历日期
// prettier-ignore
export const springFestivals = [
    [1, 31], [2, 19], [2, 8], [1, 29], [2, 16], [2, 4], [1, 25], [2, 13], [2, 2], [1, 22], // 1900-1909
    [2, 10], [1, 30], [2, 18], [2, 6], [1, 26], [2, 14], [2, 3], [1, 23], [2, 11], [2, 1], // 1910-1919
    [2, 20], [2, 8], [1, 28], [2, 16], [2, 5], [1, 24], [2, 13], [2, 2], [1, 23], [2, 10], // 1920-1929
    [1, 30], [2, 17], [2, 6], [1, 26], [2, 14], [2, 4], [1, 24], [2, 11], [1, 31], [2, 19], // 1930-1939
    [2, 8], [1, 27], [2, 15], [2, 5], [1, 25], [2, 13], [2, 2], [1, 22], [2, 10], [1, 29], // 1940-1949
    [2, 17], [2, 6], [1, 27], [2, 14], [2, 3], [1, 24], [2, 12], [1, 31], [2, 18], [2, 8], // 1950-1959
    [1, 28], [2, 15], [2, 5], [1, 25], [2, 13], [2, 2], [1, 21], [2, 9], [1, 30], [2, 17], // 1960-1969
    [2, 6], [1, 27], [2, 15], [2, 3], [1, 23], [2, 11], [1, 31], [2, 18], [2, 7], [1, 28], // 1970-1979
    [2, 16], [2, 5], [1, 25], [2, 13], [2, 2], [2, 20], [2, 9], [1, 29], [2, 17], [2, 6], // 1980-1989
    [1, 27], [2, 15], [2, 4], [1, 23], [2, 10], [1, 31], [2, 19], [2, 7], [1, 28], [2, 16], // 1990-1999
    [2, 5], [1, 24], [2, 12], [2, 1], [1, 22], [2, 9], [1, 29], [2, 18], [2, 7], [1, 26], // 2000-2009
    [2, 14], [2, 3], [1, 23], [2, 10], [1, 31], [2, 19], [2, 8], [1, 28], [2, 16], [2, 5], // 2010-2019
    [1, 25], [2, 12], [2, 1], [1, 22], [2, 10], [1, 29], [2, 17], [2, 6], [1, 26], [2, 13], // 2020-2029
    [2, 3], [1, 23], [2, 11], [1, 31], [2, 19], [2, 8], [1, 28], [2, 15], [2, 4], [1, 24], // 2030-2039
    [2, 12], [2, 1], [1, 22], [2, 10], [1, 30], [2, 17], [2, 6], [1, 26], [2, 14], [2, 2], // 2040-2049
    [1, 23], [2, 11], [2, 1], [2, 19], [2, 8], [1, 28], [2, 15], [2, 4], [1, 24], [2, 12], // 2050-2059
    [2, 2], [1, 21], [2, 9], [1, 29], [2, 17], [2, 5], [1, 26], [2, 14], [2, 3], [1, 23], // 2060-2069
    [2, 11], [1, 31], [2, 19], [2, 7], [1, 27], [2, 15], [2, 5], [1, 24], [2, 12], [2, 2], // 2070-2079
    [1, 22], [2, 9], [1, 29], [2, 17], [2, 6], [1, 26], [2, 14], [2, 3], [1, 24], [2, 10], // 2080-2089
    [1, 30], [2, 18], [2, 7], [1, 27], [2, 15], [2, 5], [1, 25], [2, 12], [2, 1], [1, 21], // 2090-2099
    [2, 9], // 2100
];
export const baseLunarYear = 1900;
/**
 * 获取农历某年闰月的月份，月份从1开始（0表示无闰月）。
 */
export function getLunarLeapMonth(year) {
    return lunarInfo[year - baseLunarYear] & 0xf;
}
/**
 * 获取农历某年闰月的天数。
 */
export function getLunarLeapMonthDays(year) {
    if (getLunarLeapMonth(year)) {
        return lunarInfo[year - baseLunarYear] & 0x10000 ? 30 : 29;
    }
    return 0;
}
/**
 * 获取农历某年的总天数。
 */
export function getLunarYearDays(year) {
    let sum = 348; // 29天*12个月
    const info = lunarInfo[year - baseLunarYear];
    // 加上大月的天数
    for (let i = 0x8000; i > 0x8; i >>= 1) {
        sum += info & i ? 1 : 0;
    }
    // 加上闰月的天数
    return sum + getLunarLeapMonthDays(year);
}
/**
 * 获取农历某年某月的天数，月份从1开始。
 */
export function getLunarMonthDays(year, month) {
    return lunarInfo[year - baseLunarYear] & (0x10000 >> month) ? 30 : 29;
}
/**
 * 公历转农历，月份从1开始（返回的闰月为负数）。
 */
export function solarToLunar(year, month, day) {
    // 计算输入日期与基准日期的天数差
    const offsetDays = Math.floor((Date.UTC(year, month - 1, day) - Date.UTC(baseLunarYear, 0, 31)) /
        (24 * 60 * 60 * 1000));
    // 农历年、月、日初始化
    let lunarYear = baseLunarYear;
    let lunarMonth = 1;
    let lunarDay = 1;
    let isLeapMonth = false;
    let daysRemaining = offsetDays;
    // 计算农历年
    while (true) {
        const yearDays = getLunarYearDays(lunarYear);
        if (daysRemaining < yearDays) {
            break;
        }
        daysRemaining -= yearDays;
        lunarYear++;
    }
    // 计算农历月和日
    const leapMonth = getLunarLeapMonth(lunarYear);
    let monthDays = 0;
    let _month = 1;
    for (; _month <= 12; _month++) {
        // 处理闰月
        if (leapMonth > 0 && _month === leapMonth + 1) {
            monthDays = getLunarLeapMonthDays(lunarYear);
            if (daysRemaining < monthDays) {
                isLeapMonth = true;
                _month--;
                break;
            }
            daysRemaining -= monthDays;
        }
        monthDays = getLunarMonthDays(lunarYear, _month);
        if (daysRemaining < monthDays) {
            break;
        }
        daysRemaining -= monthDays;
    }
    lunarMonth = _month;
    lunarDay = daysRemaining + 1;
    return {
        year: lunarYear,
        month: lunarMonth * (isLeapMonth ? -1 : 1),
        day: lunarDay,
    };
}
/**
 * 农历转公历（闰月需传递负数）。
 */
export function lunarToSolar(year, month, day) {
    const springFestival = springFestivals[year - baseLunarYear];
    const solarDate = new Date(year, springFestival[0] - 1, springFestival[1]);
    const info = lunarInfo[year - baseLunarYear];
    const leapMonth = info & 0xf;
    let totalDays = 0;
    const absMonth = Math.abs(month);
    for (let m = 1; m < absMonth; m++) {
        if (m === leapMonth) {
            totalDays += getLunarLeapMonthDays(year);
        }
        totalDays += getLunarMonthDays(year, m);
    }
    if (month < 0) {
        totalDays += getLunarMonthDays(year, absMonth);
    }
    totalDays += day - 1;
    solarDate.setDate(solarDate.getDate() + totalDays);
    return {
        year: solarDate.getFullYear(),
        month: solarDate.getMonth() + 1,
        day: solarDate.getDate(),
    };
}
// 农历年份的名称
// prettier-ignore
export const lunarYearNames = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
/**
 * 将阿拉伯数字的年份转为汉字数字年份
 */
export function getLunarYearName(year) {
    return (String(year)
        .split('')
        .map((item) => lunarYearNames[+item])
        .join('') + '年');
}
// 农历月份的名称
// prettier-ignore
export const lunarMonthNames = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
/**
 * 获取农历月份名称，月份从1开始，例如：正月，十月，腊月。
 */
export function getLunarMonthName(month, isLeapMonth) {
    return (isLeapMonth ? '闰' : '') + lunarMonthNames[month - 1] + '月';
}
// 农历日期的名称
// prettier-ignore
export const lunarDayNames = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'];
/**
 * 获取农历日期名称，例如：初一，十二，廿一。
 */
export function getLunarDayName(day) {
    return lunarDayNames[day - 1];
}
// 十天干
// prettier-ignore
export const heavenlyStems = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
// 十二地支
// prettier-ignore
export const earthlyBranches = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
/**
 * 获取农历时辰名称，例如：子初、子正、午初，午正。
 */
export function getLunarHourName(hour) {
    const index = Math.floor(((hour === 23 ? 0 : hour) + 1) / 2) % 12;
    return earthlyBranches[index] + (hour % 2 === 1 ? '初' : '正');
}
